<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think;

/**
 * ThinkPHP Model模型类
 * 实现了ORM和ActiveRecords模式
 */
class Model {
	// 操作状态
	const MODEL_INSERT = 1; // 插入模型数据
	const MODEL_UPDATE = 2; // 更新模型数据
	const MODEL_BOTH = 3; // 包含上面两种方式
	const MUST_VALIDATE = 1; // 必须验证
	const EXISTS_VALIDATE = 0; // 表单存在字段则验证
	const VALUE_VALIDATE = 2; // 表单值不为空则验证
	                          
	// 当前数据库操作对象
	protected $db = null;
	// 主键名称
	protected $pk = 'id';
	// 主键是否自动增长
	protected $autoinc = false;
	// 数据表前缀
	protected $tablePrefix = null;
	// 模型名称
	protected $name = '';
	// 数据库名称
	protected $dbName = '';
	// 数据库配置
	protected $connection = '';
	// 数据表名（不包含表前缀）
	protected $tableName = '';
	// 实际数据表名（包含表前缀）
	protected $trueTableName = '';
	// 最近错误信息
	protected $error = '';
	// 字段信息
	protected $fields = array ();
	// 数据信息
	protected $data = array ();
	// 查询表达式参数
	protected $options = array ();
	protected $_validate = array (); // 自动验证定义
	protected $_auto = array (); // 自动完成定义
	protected $_map = array (); // 字段映射定义
	protected $_scope = array (); // 命名范围定义
	                              // 是否自动检测数据表字段信息
	protected $autoCheckFields = true;
	// 是否批处理验证
	protected $patchValidate = false;
	// 链操作方法列表
	protected $methods = array (
			'strict',
			'order',
			'alias',
			'having',
			'group',
			'lock',
			'distinct',
			'auto',
			'filter',
			'validate',
			'result',
			'token',
			'index',
			'force' 
	);
	
	/**
	 * 架构函数
	 * 取得DB类的实例对象 字段检查
	 *
	 * @access public
	 * @param string $name
	 *        	模型名称
	 * @param string $tablePrefix
	 *        	表前缀
	 * @param mixed $connection
	 *        	数据库连接信息
	 */
	public function __construct($name = '', $tablePrefix = '', $connection = '') {
		// 模型初始化
		$this->_initialize ();
		// 获取模型名称
		if (! empty ( $name )) {
			if (strpos ( $name, '.' )) { // 支持 数据库名.模型名的 定义
				list ( $this->dbName, $this->name ) = explode ( '.', $name );
			} else {
				$this->name = $name;
			}
		} elseif (empty ( $this->name )) {
			$this->name = $this->getModelName ();
		}
		// 设置表前缀
		if (is_null ( $tablePrefix )) { // 前缀为Null表示没有前缀
			$this->tablePrefix = '';
		} elseif ('' != $tablePrefix) {
			$this->tablePrefix = $tablePrefix;
		} elseif (! isset ( $this->tablePrefix )) {
			$this->tablePrefix = C ( 'DB_PREFIX' );
		}
		
		// 数据库初始化操作
		// 获取数据库操作对象
		// 当前模型有独立的数据库连接信息
		$this->db ( 0, empty ( $this->connection ) ? $connection : $this->connection, true );
	}
	
	/**
	 * 自动检测数据表信息
	 *
	 * @access protected
	 * @return void
	 */
	protected function _checkTableInfo() {
		// 如果不是Model类 自动记录数据表信息
		// 只在第一次执行记录
		if (empty ( $this->fields )) {
			// 如果数据表字段没有定义则自动获取
			if (C ( 'DB_FIELDS_CACHE' )) {
				$db = $this->dbName ?  : C ( 'DB_NAME' );
				$fields = F ( '_fields/' . strtolower ( $db . '.' . $this->tablePrefix . $this->name ) );
				if ($fields) {
					$this->fields = $fields;
					if (! empty ( $fields ['_pk'] )) {
						$this->pk = $fields ['_pk'];
					}
					return;
				}
			}
			// 每次都会读取数据表信息
			$this->flush ();
		}
	}
	
	/**
	 * 获取字段信息并缓存
	 *
	 * @access public
	 * @return void
	 */
	public function flush() {
		// 缓存不存在则查询数据表信息
		$this->db->setModel ( $this->name );
		$fields = $this->db->getFields ( $this->getTableName () );
		if (! $fields) { // 无法获取字段信息
			return false;
		}
		$this->fields = array_keys ( $fields );
		unset ( $this->fields ['_pk'] );
		foreach ( $fields as $key => $val ) {
			// 记录字段类型
			$type [$key] = $val ['type'];
			if ($val ['primary']) {
				// 增加复合主键支持
				if (isset ( $this->fields ['_pk'] ) && $this->fields ['_pk'] != null) {
					if (is_string ( $this->fields ['_pk'] )) {
						$this->pk = array (
								$this->fields ['_pk'] 
						);
						$this->fields ['_pk'] = $this->pk;
					}
					$this->pk [] = $key;
					$this->fields ['_pk'] [] = $key;
				} else {
					$this->pk = $key;
					$this->fields ['_pk'] = $key;
				}
				if ($val ['autoinc'])
					$this->autoinc = true;
			}
		}
		// 记录字段类型信息
		$this->fields ['_type'] = $type;
		
		// 2008-3-7 增加缓存开关控制
		if (C ( 'DB_FIELDS_CACHE' )) {
			// 永久缓存数据表信息
			$db = $this->dbName ?  : C ( 'DB_NAME' );
			F ( '_fields/' . strtolower ( $db . '.' . $this->tablePrefix . $this->name ), $this->fields );
		}
	}
	
	/**
	 * 设置数据对象的值
	 *
	 * @access public
	 * @param string $name
	 *        	名称
	 * @param mixed $value
	 *        	值
	 * @return void
	 */
	public function __set($name, $value) {
		// 设置数据对象属性
		$this->data [$name] = $value;
	}
	
	/**
	 * 获取数据对象的值
	 *
	 * @access public
	 * @param string $name
	 *        	名称
	 * @return mixed
	 */
	public function __get($name) {
		return isset ( $this->data [$name] ) ? $this->data [$name] : null;
	}
	
	/**
	 * 检测数据对象的值
	 *
	 * @access public
	 * @param string $name
	 *        	名称
	 * @return boolean
	 */
	public function __isset($name) {
		return isset ( $this->data [$name] );
	}
	
	/**
	 * 销毁数据对象的值
	 *
	 * @access public
	 * @param string $name
	 *        	名称
	 * @return void
	 */
	public function __unset($name) {
		unset ( $this->data [$name] );
	}
	
	/**
	 * 利用__call方法实现一些特殊的Model方法
	 *
	 * @access public
	 * @param string $method
	 *        	方法名称
	 * @param array $args
	 *        	调用参数
	 * @return mixed
	 */
	public function __call($method, $args) {
		if (in_array ( strtolower ( $method ), $this->methods, true )) {
			// 连贯操作的实现
			$this->options [strtolower ( $method )] = $args [0];
			return $this;
		} elseif (in_array ( strtolower ( $method ), array (
				'count',
				'sum',
				'min',
				'max',
				'avg' 
		), true )) {
			// 统计查询的实现
			$field = isset ( $args [0] ) ? $args [0] : '*';
			return $this->getField ( strtoupper ( $method ) . '(' . $field . ') AS tp_' . $method );
		} elseif (strtolower ( substr ( $method, 0, 5 ) ) == 'getby') {
			// 根据某个字段获取记录
			$field = parse_name ( substr ( $method, 5 ) );
			$where [$field] = $args [0];
			return $this->where ( $where )->find ();
		} elseif (strtolower ( substr ( $method, 0, 10 ) ) == 'getfieldby') {
			// 根据某个字段获取记录的某个值
			$name = parse_name ( substr ( $method, 10 ) );
			$where [$name] = $args [0];
			return $this->where ( $where )->getField ( $args [1] );
		} elseif (isset ( $this->_scope [$method] )) { // 命名范围的单独调用支持
			return $this->scope ( $method, $args [0] );
		} else {
			E ( __CLASS__ . ':' . $method . L ( '_METHOD_NOT_EXIST_' ) );
			return;
		}
	}
	// 回调方法 初始化模型
	protected function _initialize() {
	}
	
	/**
	 * 对保存到数据库的数据进行处理
	 *
	 * @access protected
	 * @param mixed $data
	 *        	要操作的数据
	 * @return boolean
	 */
	protected function _facade($data) {
		
		// 检查数据字段合法性
		if (! empty ( $this->fields )) {
			if (! empty ( $this->options ['field'] )) {
				$fields = $this->options ['field'];
				unset ( $this->options ['field'] );
				if (is_string ( $fields )) {
					$fields = explode ( ',', $fields );
				}
			} else {
				$fields = $this->fields;
			}
			foreach ( $data as $key => $val ) {
				if (! in_array ( $key, $fields, true )) {
					if (! empty ( $this->options ['strict'] )) {
						E ( L ( '_DATA_TYPE_INVALID_' ) . ':[' . $key . '=>' . $val . ']' );
					}
					unset ( $data [$key] );
				} elseif (is_scalar ( $val )) {
					// 字段类型检查 和 强制转换
					$this->_parseType ( $data, $key );
				}
			}
		}
		
		// 安全过滤
		if (! empty ( $this->options ['filter'] )) {
			$data = array_map ( $this->options ['filter'], $data );
			unset ( $this->options ['filter'] );
		}
		$this->_before_write ( $data );
		return $data;
	}
	
	// 写入数据前的回调方法 包括新增和更新
	protected function _before_write(&$data) {
	}
	
	/**
	 * 新增数据
	 *
	 * @access public
	 * @param mixed $data
	 *        	数据
	 * @param array $options
	 *        	表达式
	 * @param boolean $replace
	 *        	是否replace
	 * @return mixed
	 */
	public function add($data = '', $options = array(), $replace = false) {
		if (empty ( $data )) {
			// 没有传递数据，获取当前数据对象的值
			if (! empty ( $this->data )) {
				$data = $this->data;
				// 重置数据
				$this->data = array ();
			} else {
				$this->error = L ( '_DATA_TYPE_INVALID_' );
				return false;
			}
		}
		// 数据处理
		$data = $this->_facade ( $data );
		// 分析表达式
		$options = $this->_parseOptions ( $options );
		if (false === $this->_before_insert ( $data, $options )) {
			return false;
		}
		// 写入数据到数据库
		$result = $this->db->insert ( $data, $options, $replace );
		if (false !== $result && is_numeric ( $result )) {
			$pk = $this->getPk ();
			// 增加复合主键支持
			if (is_array ( $pk ))
				return $result;
			$insertId = $this->getLastInsID ();
			if ($insertId) {
				// 自增主键返回插入ID
				$data [$pk] = $insertId;
				if (false === $this->_after_insert ( $data, $options )) {
					return false;
				}
				return $insertId;
			}
			if (false === $this->_after_insert ( $data, $options )) {
				return false;
			}
		}
		return $result;
	}
	// 插入数据前的回调方法
	protected function _before_insert(&$data, $options) {
	}
	// 插入成功后的回调方法
	protected function _after_insert($data, $options) {
	}
	public function addAll($dataList, $options = array(), $replace = false) {
		if (empty ( $dataList )) {
			$this->error = L ( '_DATA_TYPE_INVALID_' );
			return false;
		}
		// 数据处理
		foreach ( $dataList as $key => $data ) {
			$dataList [$key] = $this->_facade ( $data );
		}
		// 分析表达式
		$options = $this->_parseOptions ( $options );
		// 写入数据到数据库
		$result = $this->db->insertAll ( $dataList, $options, $replace );
		if (false !== $result) {
			$insertId = $this->getLastInsID ();
			if ($insertId) {
				return $insertId;
			}
		}
		return $result;
	}
	/*
	 * 在大并时优化插入数据性能，该函数需要两次调用：
	 * 一是在插入数据的地方调用，这时table和data两个参数不能为空
	 * 二是管理在后台查看列表时调用，这时table参数不为空，但data为空，这些防止有部分数据在缓存里无法到达数据库
	 */
	function delayAdd($data = array(), $delay = 10) {
		return $this->add ( $data );
		
		$table = $this->tableName;
		$data_key = 'common_delayAdd_data_' . $table;
		$time_key = 'common_delayAdd_time_' . $table;
		
		$last_time = S ( $time_key );
		$dataArr = ( array ) S ( $data_key );
		empty ( $data ) || $dataArr [] = $data;
		if ($last_time === false) {
			$last_time = NOW_TIME;
			S ( $time_key, $last_time );
		}
		
		if (! empty ( $data ) && $delay > 0 && NOW_TIME - $last_time < $delay) {
			$msg ['status'] = S ( $data_key, $dataArr );
			$msg ['msg'] = 'add_cache';
		} elseif (! empty ( $dataArr )) {
			$msg ['status'] = $this->addAll ( $dataArr );
			
			if ($msg ['status']) {
				S ( $data_key, array () );
				S ( $time_key, NOW_TIME );
				$msg ['msg'] = 'add_db_success';
			} else {
				$msg ['msg'] = $this->getLastSql ();
			}
		} else {
			$msg ['status'] = 0;
			$msg ['msg'] = 'No data install';
		}
		
		return $msg;
	}
	/**
	 * 通过Select方式添加记录
	 *
	 * @access public
	 * @param string $fields
	 *        	要插入的数据表字段名
	 * @param string $table
	 *        	要插入的数据表名
	 * @param array $options
	 *        	表达式
	 * @return boolean
	 */
	public function selectAdd($fields = '', $table = '', $options = array()) {
		// 分析表达式
		$options = $this->_parseOptions ( $options );
		// 写入数据到数据库
		if (false === $result = $this->db->selectInsert ( $fields ?  : $options ['field'], $table ?  : $this->getTableName (), $options )) {
			// 数据库插入操作失败
			$this->error = L ( '_OPERATION_WRONG_' );
			return false;
		} else {
			// 插入成功
			return $result;
		}
	}
	
	/**
	 * 保存数据
	 *
	 * @access public
	 * @param mixed $data
	 *        	数据
	 * @param array $options
	 *        	表达式
	 * @return boolean
	 */
	public function save($data = '', $options = array()) {
		if (empty ( $data )) {
			// 没有传递数据，获取当前数据对象的值
			if (! empty ( $this->data )) {
				$data = $this->data;
				// 重置数据
				$this->data = array ();
			} else {
				$this->error = L ( '_DATA_TYPE_INVALID_' );
				return false;
			}
		}
		// 数据处理
		$data = $this->_facade ( $data );
		if (empty ( $data )) {
			// 没有数据则不执行
			$this->error = L ( '_DATA_TYPE_INVALID_' );
			return false;
		}
		// 分析表达式
		$options = $this->_parseOptions ( $options );
		$pk = $this->getPk ();
		if (! isset ( $options ['where'] )) {
			// 如果存在主键数据 则自动作为更新条件
			if (is_string ( $pk ) && isset ( $data [$pk] )) {
				$where [$pk] = $data [$pk];
				unset ( $data [$pk] );
			} elseif (is_array ( $pk )) {
				// 增加复合主键支持
				foreach ( $pk as $field ) {
					if (isset ( $data [$pk] )) {
						$where [$field] = $data [$field];
					} else {
						// 如果缺少复合主键数据则不执行
						$this->error = L ( '_OPERATION_WRONG_' );
						return false;
					}
					unset ( $data [$pk] );
				}
			}
			if (! isset ( $where )) {
				// 如果没有任何更新条件则不执行
				$this->error = L ( '_OPERATION_WRONG_' );
				return false;
			} else {
				$options ['where'] = $where;
			}
		}
		
		if (is_array ( $options ['where'] ) && isset ( $options ['where'] [$pk] )) {
			$pkValue = $options ['where'] [$pk];
		}
		if (false === $this->_before_update ( $data, $options )) {
			return false;
		}
		$result = $this->db->update ( $data, $options );
		if (false !== $result && is_numeric ( $result )) {
			if (isset ( $pkValue ))
				$data [$pk] = $pkValue;
			$this->_after_update ( $data, $options );
		}
		return $result;
	}
	// 更新数据前的回调方法
	protected function _before_update(&$data, $options) {
	}
	// 更新成功后的回调方法
	protected function _after_update($data, $options) {
	}
	
	/**
	 * 删除数据
	 *
	 * @access public
	 * @param mixed $options
	 *        	表达式
	 * @return mixed
	 */
	public function delete($options = array()) {
		$pk = $this->getPk ();
		if (empty ( $options ) && empty ( $this->options ['where'] )) {
			// 如果删除条件为空 则删除当前数据对象所对应的记录
			if (! empty ( $this->data ) && isset ( $this->data [$pk] ))
				return $this->delete ( $this->data [$pk] );
			else
				return false;
		}
		if (is_numeric ( $options ) || is_string ( $options )) {
			// 根据主键删除记录
			if (strpos ( $options, ',' )) {
				$where [$pk] = array (
						'IN',
						$options 
				);
			} else {
				$where [$pk] = $options;
			}
			$options = array ();
			$options ['where'] = $where;
		}
		// 根据复合主键删除记录
		if (is_array ( $options ) && (count ( $options ) > 0) && is_array ( $pk )) {
			$count = 0;
			foreach ( array_keys ( $options ) as $key ) {
				if (is_int ( $key ))
					$count ++;
			}
			if ($count == count ( $pk )) {
				$i = 0;
				foreach ( $pk as $field ) {
					$where [$field] = $options [$i];
					unset ( $options [$i ++] );
				}
				$options ['where'] = $where;
			} else {
				return false;
			}
		}
		// 分析表达式
		$options = $this->_parseOptions ( $options );
		if (empty ( $options ['where'] )) {
			// 如果条件为空 不进行删除操作 除非设置 1=1
			return false;
		}
		if (is_array ( $options ['where'] ) && isset ( $options ['where'] [$pk] )) {
			$pkValue = $options ['where'] [$pk];
		}
		
		if (false === $this->_before_delete ( $options )) {
			return false;
		}
		$result = $this->db->delete ( $options );
		if (false !== $result && is_numeric ( $result )) {
			$data = array ();
			if (isset ( $pkValue ))
				$data [$pk] = $pkValue;
			$this->_after_delete ( $data, $options );
		}
		// 返回删除记录个数
		return $result;
	}
	// 删除数据前的回调方法
	protected function _before_delete($options) {
	}
	// 删除成功后的回调方法
	protected function _after_delete($data, $options) {
	}
	
	/**
	 * 查询数据集
	 *
	 * @access public
	 * @param array $options
	 *        	表达式参数
	 * @return mixed
	 */
	public function select($options = array()) {
		$pk = $this->getPk ();
		if (is_string ( $options ) || is_numeric ( $options )) {
			// 根据主键查询
			if (strpos ( $options, ',' )) {
				$where [$pk] = array (
						'IN',
						$options 
				);
			} else {
				$where [$pk] = $options;
			}
			$options = array ();
			$options ['where'] = $where;
		} elseif (is_array ( $options ) && (count ( $options ) > 0) && is_array ( $pk )) {
			// 根据复合主键查询
			$count = 0;
			foreach ( array_keys ( $options ) as $key ) {
				if (is_int ( $key ))
					$count ++;
			}
			if ($count == count ( $pk )) {
				$i = 0;
				foreach ( $pk as $field ) {
					$where [$field] = $options [$i];
					unset ( $options [$i ++] );
				}
				$options ['where'] = $where;
			} else {
				return false;
			}
		} elseif (false === $options) { // 用于子查询 不查询只返回SQL
			$options = array ();
			// 分析表达式
			$options = $this->_parseOptions ( $options );
			return '( ' . $this->fetchSql ( true )->select ( $options ) . ' )';
		}
		// 分析表达式
		$options = $this->_parseOptions ( $options );
		// 判断查询缓存
		if (isset ( $options ['cache'] )) {
			$cache = $options ['cache'];
			$key = is_string ( $cache ['key'] ) ? $cache ['key'] : md5 ( serialize ( $options ) );
			$data = S ( $key, '', $cache );
			if (false !== $data) {
				return $data;
			}
		}
		$resultSet = $this->db->select ( $options );
		if (false === $resultSet) {
			return false;
		}
		if (empty ( $resultSet )) { // 查询结果为空
			return null;
		}
		
		if (is_string ( $resultSet )) {
			return $resultSet;
		}
		
		$resultSet = array_map ( array (
				$this,
				'_read_data' 
		), $resultSet );
		$this->_after_select ( $resultSet, $options );
		if (isset ( $options ['index'] )) { // 对数据集进行索引
			$index = explode ( ',', $options ['index'] );
			foreach ( $resultSet as $result ) {
				$_key = $result [$index [0]];
				if (isset ( $index [1] ) && isset ( $result [$index [1]] )) {
					$cols [$_key] = $result [$index [1]];
				} else {
					$cols [$_key] = $result;
				}
			}
			$resultSet = $cols;
		}
		if (isset ( $cache )) {
			S ( $key, $resultSet, $cache );
		}
		return $resultSet;
	}
	// 查询成功后的回调方法
	protected function _after_select(&$resultSet, $options) {
	}
	
	/**
	 * 生成查询SQL 可用于子查询
	 *
	 * @access public
	 * @return string
	 */
	public function buildSql() {
		return '( ' . $this->fetchSql ( true )->select () . ' )';
	}
	
	/**
	 * 分析表达式
	 *
	 * @access protected
	 * @param array $options
	 *        	表达式参数
	 * @return array
	 */
	protected function _parseOptions($options = array()) {
		if (is_array ( $options ))
			$options = array_merge ( $this->options, $options );
		
		if (! isset ( $options ['table'] )) {
			// 自动获取表名
			$options ['table'] = $this->getTableName ();
			$fields = $this->fields;
		} else {
			// 指定数据表 则重新获取字段列表 但不支持类型检测
			$fields = $this->getDbFields ();
		}
		
		// 数据表别名
		if (! empty ( $options ['alias'] )) {
			$options ['table'] .= ' ' . $options ['alias'];
		}
		// 记录操作的模型名称
		$options ['model'] = $this->name;
		
		// 字段类型验证
		if (isset ( $options ['where'] ) && is_array ( $options ['where'] ) && ! empty ( $fields ) && ! isset ( $options ['join'] )) {
			// 对数组查询条件进行字段类型检查
			foreach ( $options ['where'] as $key => $val ) {
				$key = trim ( $key );
				if (in_array ( $key, $fields, true )) {
					if (is_scalar ( $val )) {
						$this->_parseType ( $options ['where'], $key );
					}
				} elseif (! is_numeric ( $key ) && '_' != substr ( $key, 0, 1 ) && false === strpos ( $key, '.' ) && false === strpos ( $key, '(' ) && false === strpos ( $key, '|' ) && false === strpos ( $key, '&' )) {
					if (! empty ( $this->options ['strict'] )) {
						E ( L ( '_ERROR_QUERY_EXPRESS_' ) . ':[' . $key . '=>' . $val . ']' );
					}
					unset ( $options ['where'] [$key] );
				}
			}
		}
		// 查询过后清空sql表达式组装 避免影响下次查询
		$this->options = array ();
		// 表达式过滤
		$this->_options_filter ( $options );
		return $options;
	}
	// 表达式过滤回调方法
	protected function _options_filter(&$options) {
	}
	
	/**
	 * 数据类型检测
	 *
	 * @access protected
	 * @param mixed $data
	 *        	数据
	 * @param string $key
	 *        	字段名
	 * @return void
	 */
	protected function _parseType(&$data, $key) {
		if (! isset ( $this->options ['bind'] [':' . $key] ) && isset ( $this->fields ['_type'] [$key] )) {
			$fieldType = strtolower ( $this->fields ['_type'] [$key] );
			if (false !== strpos ( $fieldType, 'enum' )) {
				// 支持ENUM类型优先检测
			} elseif (false === strpos ( $fieldType, 'bigint' ) && false !== strpos ( $fieldType, 'int' )) {
				$data [$key] = intval ( $data [$key] );
			} elseif (false !== strpos ( $fieldType, 'float' ) || false !== strpos ( $fieldType, 'double' )) {
				$data [$key] = floatval ( $data [$key] );
			} elseif (false !== strpos ( $fieldType, 'bool' )) {
				$data [$key] = ( bool ) $data [$key];
			}
		}
	}
	
	/**
	 * 数据读取后的处理
	 *
	 * @access protected
	 * @param array $data
	 *        	当前数据
	 * @return array
	 */
	protected function _read_data($data) {
		// 检查字段映射
		if (! empty ( $this->_map ) && C ( 'READ_DATA_MAP' )) {
			foreach ( $this->_map as $key => $val ) {
				if (isset ( $data [$val] )) {
					$data [$key] = $data [$val];
					unset ( $data [$val] );
				}
			}
		}
		return $data;
	}
	
	/**
	 * 查询数据
	 *
	 * @access public
	 * @param mixed $options
	 *        	表达式参数
	 * @return mixed
	 */
	public function find($options = array()) {
		if (is_numeric ( $options ) || is_string ( $options )) {
			$where [$this->getPk ()] = $options;
			$options = array ();
			$options ['where'] = $where;
		}
		// 根据复合主键查找记录
		$pk = $this->getPk ();
		if (is_array ( $options ) && (count ( $options ) > 0) && is_array ( $pk )) {
			// 根据复合主键查询
			$count = 0;
			foreach ( array_keys ( $options ) as $key ) {
				if (is_int ( $key ))
					$count ++;
			}
			if ($count == count ( $pk )) {
				$i = 0;
				foreach ( $pk as $field ) {
					$where [$field] = $options [$i];
					unset ( $options [$i ++] );
				}
				$options ['where'] = $where;
			} else {
				return false;
			}
		}
		// 总是查找一条记录
		$options ['limit'] = 1;
		// 分析表达式
		$options = $this->_parseOptions ( $options );
		// 判断查询缓存
		if (isset ( $options ['cache'] )) {
			$cache = $options ['cache'];
			$key = is_string ( $cache ['key'] ) ? $cache ['key'] : md5 ( serialize ( $options ) );
			$data = S ( $key, '', $cache );
			if (false !== $data) {
				$this->data = $data;
				return $data;
			}
		}
		$resultSet = $this->db->select ( $options );
		if (false === $resultSet) {
			return false;
		}
		if (empty ( $resultSet )) { // 查询结果为空
			return null;
		}
		if (is_string ( $resultSet )) {
			return $resultSet;
		}
		
		// 读取数据后的处理
		$data = $this->_read_data ( $resultSet [0] );
		$this->_after_find ( $data, $options );
		if (! empty ( $this->options ['result'] )) {
			return $this->returnResult ( $data, $this->options ['result'] );
		}
		$this->data = $data;
		if (isset ( $cache )) {
			S ( $key, $data, $cache );
		}
		return $this->data;
	}
	// 查询成功的回调方法
	protected function _after_find(&$result, $options) {
	}
	protected function returnResult($data, $type = '') {
		if ($type) {
			if (is_callable ( $type )) {
				return call_user_func ( $type, $data );
			}
			switch (strtolower ( $type )) {
				case 'json' :
					return json_encode ( $data );
				case 'xml' :
					return xml_encode ( $data );
			}
		}
		return $data;
	}
	
	/**
	 * 处理字段映射
	 *
	 * @access public
	 * @param array $data
	 *        	当前数据
	 * @param integer $type
	 *        	类型 0 写入 1 读取
	 * @return array
	 */
	public function parseFieldsMap($data, $type = 1) {
		// 检查字段映射
		if (! empty ( $this->_map )) {
			foreach ( $this->_map as $key => $val ) {
				if ($type == 1) { // 读取
					if (isset ( $data [$val] )) {
						$data [$key] = $data [$val];
						unset ( $data [$val] );
					}
				} else {
					if (isset ( $data [$key] )) {
						$data [$val] = $data [$key];
						unset ( $data [$key] );
					}
				}
			}
		}
		return $data;
	}
	
	/**
	 * 设置记录的某个字段值
	 * 支持使用数据库字段和方法
	 *
	 * @access public
	 * @param string|array $field
	 *        	字段名
	 * @param string $value
	 *        	字段值
	 * @return boolean
	 */
	public function setField($field, $value = '') {
		if (is_array ( $field )) {
			$data = $field;
		} else {
			$data [$field] = $value;
		}
		return $this->save ( $data );
	}
	
	/**
	 * 字段值增长
	 *
	 * @access public
	 * @param string $field
	 *        	字段名
	 * @param integer $step
	 *        	增长值
	 * @param integer $lazyTime
	 *        	延时时间(s)
	 * @return boolean
	 */
	public function setInc($field, $step = 1, $lazyTime = 0) {
		if ($lazyTime > 0) { // 延迟写入
			$condition = $this->options ['where'];
			$guid = md5 ( $this->name . '_' . $field . '_' . serialize ( $condition ) );
			$step = $this->lazyWrite ( $guid, $step, $lazyTime );
			if (false === $step)
				return true; // 等待下次写入
		}
		return $this->setField ( $field, array (
				'exp',
				$field . '+' . $step 
		) );
	}
	
	/**
	 * 字段值减少
	 *
	 * @access public
	 * @param string $field
	 *        	字段名
	 * @param integer $step
	 *        	减少值
	 * @param integer $lazyTime
	 *        	延时时间(s)
	 * @return boolean
	 */
	public function setDec($field, $step = 1, $lazyTime = 0) {
		if ($lazyTime > 0) { // 延迟写入
			$condition = $this->options ['where'];
			$guid = md5 ( $this->name . '_' . $field . '_' . serialize ( $condition ) );
			$step = $this->lazyWrite ( $guid, $step, $lazyTime );
			if (false === $step)
				return true; // 等待下次写入
		}
		return $this->setField ( $field, array (
				'exp',
				$field . '-' . $step 
		) );
	}
	
	/**
	 * 延时更新检查 返回false表示需要延时
	 * 否则返回实际写入的数值
	 *
	 * @access public
	 * @param string $guid
	 *        	写入标识
	 * @param integer $step
	 *        	写入步进值
	 * @param integer $lazyTime
	 *        	延时时间(s)
	 * @return false|integer
	 */
	protected function lazyWrite($guid, $step, $lazyTime) {
		if (false !== ($value = S ( $guid ))) { // 存在缓存写入数据
			if (NOW_TIME > S ( $guid . '_time' ) + $lazyTime) {
				// 延时更新时间到了，删除缓存数据 并实际写入数据库
				S ( $guid, NULL );
				S ( $guid . '_time', NULL );
				return $value + $step;
			} else {
				// 追加数据到缓存
				S ( $guid, $value + $step );
				return false;
			}
		} else { // 没有缓存数据
			S ( $guid, $step );
			// 计时开始
			S ( $guid . '_time', NOW_TIME );
			return false;
		}
	}
	
	/**
	 * 获取一条记录的某个字段值
	 *
	 * @access public
	 * @param string $field
	 *        	字段名
	 * @param string $spea
	 *        	字段数据间隔符号 NULL返回数组
	 * @return mixed
	 */
	public function getField($field, $sepa = null) {
		$options ['field'] = $field;
		$options = $this->_parseOptions ( $options );
		// 判断查询缓存
		if (isset ( $options ['cache'] )) {
			$cache = $options ['cache'];
			$key = is_string ( $cache ['key'] ) ? $cache ['key'] : md5 ( $sepa . serialize ( $options ) );
			$data = S ( $key, '', $cache );
			if (false !== $data) {
				return $data;
			}
		}
		$field = trim ( $field );
		if (strpos ( $field, ',' ) && false !== $sepa) { // 多字段
			if (! isset ( $options ['limit'] )) {
				$options ['limit'] = is_numeric ( $sepa ) ? $sepa : '';
			}
			$resultSet = $this->db->select ( $options );
			if (! empty ( $resultSet )) {
				$_field = explode ( ',', $field );
				$field = array_keys ( $resultSet [0] );
				$key1 = array_shift ( $field );
				$key2 = array_shift ( $field );
				$cols = array ();
				$count = count ( $_field );
				foreach ( $resultSet as $result ) {
					$name = $result [$key1];
					if (2 == $count) {
						$cols [$name] = $result [$key2];
					} else {
						$cols [$name] = is_string ( $sepa ) ? implode ( $sepa, array_slice ( $result, 1 ) ) : $result;
					}
				}
				if (isset ( $cache )) {
					S ( $key, $cols, $cache );
				}
				return $cols;
			}
		} else { // 查找一条记录
		         // 返回数据个数
			if (true !== $sepa) { // 当sepa指定为true的时候 返回所有数据
				$options ['limit'] = is_numeric ( $sepa ) ? $sepa : 1;
			}
			$result = $this->db->select ( $options );
			if (! empty ( $result )) {
				if (true !== $sepa && 1 == $options ['limit']) {
					$data = reset ( $result [0] );
					if (isset ( $cache )) {
						S ( $key, $data, $cache );
					}
					return $data;
				}
				foreach ( $result as $val ) {
					$array [] = $val [$field];
				}
				if (isset ( $cache )) {
					S ( $key, $array, $cache );
				}
				return $array;
			}
		}
		return null;
	}
	
	/**
	 * 创建数据对象 但不保存到数据库
	 *
	 * @access public
	 * @param mixed $data
	 *        	创建数据
	 * @param string $type
	 *        	状态
	 * @return mixed
	 */
	public function create($data = '', $type = '') {
		// 如果没有传值默认取POST数据
		if (empty ( $data )) {
			$data = I ( 'post.' );
		} elseif (is_object ( $data )) {
			$data = get_object_vars ( $data );
		}
		// 验证数据
		if (empty ( $data ) || ! is_array ( $data )) {
			$this->error = L ( '_DATA_TYPE_INVALID_' );
			return false;
		}
		
		// 状态
		$type = $type ?  : (! empty ( $data [$this->getPk ()] ) ? self::MODEL_UPDATE : self::MODEL_INSERT);
		
		// 检查字段映射
		if (! empty ( $this->_map )) {
			foreach ( $this->_map as $key => $val ) {
				if (isset ( $data [$key] )) {
					$data [$val] = $data [$key];
					unset ( $data [$key] );
				}
			}
		}
		
		// 检测提交字段的合法性
		if (isset ( $this->options ['field'] )) { // $this->field('field1,field2...')->create()
			$fields = $this->options ['field'];
			unset ( $this->options ['field'] );
		} elseif ($type == self::MODEL_INSERT && isset ( $this->insertFields )) {
			$fields = $this->insertFields;
		} elseif ($type == self::MODEL_UPDATE && isset ( $this->updateFields )) {
			$fields = $this->updateFields;
		}
		if (isset ( $fields )) {
			if (is_string ( $fields )) {
				$fields = explode ( ',', $fields );
			}
			// 判断令牌验证字段
			if (C ( 'TOKEN_ON' ))
				$fields [] = C ( 'TOKEN_NAME', null, '__hash__' );
			foreach ( $data as $key => $val ) {
				if (! in_array ( $key, $fields )) {
					unset ( $data [$key] );
				}
			}
		}
		
		// 数据自动验证
		if (! $this->autoValidation ( $data, $type ))
			return false;
			
			// 表单令牌验证
		if (! $this->autoCheckToken ( $data )) {
			$this->error = L ( '_TOKEN_ERROR_' );
			return false;
		}
		
		// 验证完成生成数据对象
		if ($this->autoCheckFields) { // 开启字段检测 则过滤非法字段数据
			$fields = $this->getDbFields ();
			foreach ( $data as $key => $val ) {
				if (! in_array ( $key, $fields )) {
					unset ( $data [$key] );
				} elseif (MAGIC_QUOTES_GPC && is_string ( $val )) {
					$data [$key] = stripslashes ( $val );
				}
			}
		}
		
		// 创建完成对数据进行自动处理
		$this->autoOperation ( $data, $type );
		// 赋值当前数据对象
		$this->data = $data;
		// 返回创建的数据以供其他调用
		return $data;
	}
	
	// 自动表单令牌验证
	// TODO ajax无刷新多次提交暂不能满足
	public function autoCheckToken($data) {
		// 支持使用token(false) 关闭令牌验证
		if (isset ( $this->options ['token'] ) && ! $this->options ['token'])
			return true;
		if (C ( 'TOKEN_ON' )) {
			$name = C ( 'TOKEN_NAME', null, '__hash__' );
			if (! isset ( $data [$name] ) || ! isset ( $_SESSION [$name] )) { // 令牌数据无效
				return false;
			}
			
			// 令牌验证
			list ( $key, $value ) = explode ( '_', $data [$name] );
			if ($value && $_SESSION [$name] [$key] === $value) { // 防止重复提交
				unset ( $_SESSION [$name] [$key] ); // 验证完成销毁session
				return true;
			}
			// 开启TOKEN重置
			if (C ( 'TOKEN_RESET' ))
				unset ( $_SESSION [$name] [$key] );
			return false;
		}
		return true;
	}
	
	/**
	 * 使用正则验证数据
	 *
	 * @access public
	 * @param string $value
	 *        	要验证的数据
	 * @param string $rule
	 *        	验证规则
	 * @return boolean
	 */
	public function regex($value, $rule) {
		// weiphp 解决设置模型字段设置多选和必填时验证失败的问题
		if (is_array ( $value ) && $rule == 'require') {
			return ! empty ( $value );
		}
		$validate = array (
				'require' => '/\S+/',
				'email' => '/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/',
				'url' => '/^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(:\d+)?(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?$/',
				'currency' => '/^\d+(\.\d+)?$/',
				'number' => '/^\d+$/',
				'zip' => '/^\d{6}$/',
				'integer' => '/^[-\+]?\d+$/',
				'double' => '/^[-\+]?\d+(\.\d+)?$/',
				'english' => '/^[A-Za-z]+$/' 
		);
		// 检查是否有内置的正则表达式
		if (isset ( $validate [strtolower ( $rule )] ))
			$rule = $validate [strtolower ( $rule )];
		return preg_match ( $rule, $value ) === 1;
	}
	
	/**
	 * 自动表单处理
	 *
	 * @access public
	 * @param array $data
	 *        	创建数据
	 * @param string $type
	 *        	创建类型
	 * @return mixed
	 */
	private function autoOperation(&$data, $type) {
		if (! empty ( $this->options ['auto'] )) {
			$_auto = $this->options ['auto'];
			unset ( $this->options ['auto'] );
		} elseif (! empty ( $this->_auto )) {
			$_auto = $this->_auto;
		}
		// 自动填充
		if (isset ( $_auto )) {
			foreach ( $_auto as $auto ) {
				// 填充因子定义格式
				// array('field','填充内容','填充条件','附加规则',[额外参数])
				if (empty ( $auto [2] ))
					$auto [2] = self::MODEL_INSERT; // 默认为新增的时候自动填充
				if ($type == $auto [2] || $auto [2] == self::MODEL_BOTH) {
					if (empty ( $auto [3] ))
						$auto [3] = 'string';
					switch (trim ( $auto [3] )) {
						case 'function' : // 使用函数进行填充 字段的值作为参数
						case 'callback' : // 使用回调方法
							$args = isset ( $auto [4] ) ? ( array ) $auto [4] : array ();
							if (isset ( $data [$auto [0]] )) {
								array_unshift ( $args, $data [$auto [0]] );
							}
							if ('function' == $auto [3]) {
								$data [$auto [0]] = call_user_func_array ( $auto [1], $args );
							} else {
								$data [$auto [0]] = call_user_func_array ( array (
										&$this,
										$auto [1] 
								), $args );
							}
							break;
						case 'field' : // 用其它字段的值进行填充
							$data [$auto [0]] = $data [$auto [1]];
							break;
						case 'ignore' : // 为空忽略
							if ($auto [1] === $data [$auto [0]])
								unset ( $data [$auto [0]] );
							break;
						case 'string' :
						default : // 默认作为字符串填充
							$data [$auto [0]] = $auto [1];
					}
					if (isset ( $data [$auto [0]] ) && false === $data [$auto [0]])
						unset ( $data [$auto [0]] );
				}
			}
		}
		return $data;
	}
	
	/**
	 * 自动表单验证
	 *
	 * @access protected
	 * @param array $data
	 *        	创建数据
	 * @param string $type
	 *        	创建类型
	 * @return boolean
	 */
	protected function autoValidation($data, $type) {
		if (! empty ( $this->options ['validate'] )) {
			$_validate = $this->options ['validate'];
			unset ( $this->options ['validate'] );
		} elseif (! empty ( $this->_validate )) {
			$_validate = $this->_validate;
		}
		// 属性验证
		if (isset ( $_validate )) { // 如果设置了数据自动验证则进行数据验证
			if ($this->patchValidate) { // 重置验证错误信息
				$this->error = array ();
			}
			foreach ( $_validate as $key => $val ) {
				// 验证因子定义格式
				// array(field,rule,message,condition,type,when,params)
				// 判断是否需要执行验证
				if (empty ( $val [5] ) || $val [5] == self::MODEL_BOTH || $val [5] == $type) {
					if (0 == strpos ( $val [2], '{%' ) && strpos ( $val [2], '}' ))
						
						// 支持提示信息的多语言 使用 {%语言定义} 方式
						$val [2] = L ( substr ( $val [2], 2, - 1 ) );
					$val [3] = isset ( $val [3] ) ? $val [3] : self::EXISTS_VALIDATE;
					$val [4] = isset ( $val [4] ) ? $val [4] : 'regex';
					// 判断验证条件
					switch ($val [3]) {
						case self::MUST_VALIDATE : // 必须验证 不管表单是否有设置该字段
							if (false === $this->_validationField ( $data, $val ))
								return false;
							break;
						case self::VALUE_VALIDATE : // 值不为空的时候才验证
							if ('' != trim ( $data [$val [0]] ))
								if (false === $this->_validationField ( $data, $val ))
									return false;
							break;
						default : // 默认表单存在该字段就验证
							if (isset ( $data [$val [0]] ))
								if (false === $this->_validationField ( $data, $val ))
									return false;
					}
				}
			}
			// 批量验证的时候最后返回错误
			if (! empty ( $this->error ))
				return false;
		}
		return true;
	}
	
	/**
	 * 验证表单字段 支持批量验证
	 * 如果批量验证返回错误的数组信息
	 *
	 * @access protected
	 * @param array $data
	 *        	创建数据
	 * @param array $val
	 *        	验证因子
	 * @return boolean
	 */
	protected function _validationField($data, $val) {
		if ($this->patchValidate && isset ( $this->error [$val [0]] ))
			return; // 当前字段已经有规则验证没有通过
		if (false === $this->_validationFieldItem ( $data, $val )) {
			if ($this->patchValidate) {
				$this->error [$val [0]] = $val [2];
			} else {
				$this->error = $val [2];
				return false;
			}
		}
		return;
	}
	
	/**
	 * 根据验证因子验证字段
	 *
	 * @access protected
	 * @param array $data
	 *        	创建数据
	 * @param array $val
	 *        	验证因子
	 * @return boolean
	 */
	protected function _validationFieldItem($data, $val) {
		switch (strtolower ( trim ( $val [4] ) )) {
			case 'function' : // 使用函数进行验证
			case 'callback' : // 调用方法进行验证
				$args = isset ( $val [6] ) ? ( array ) $val [6] : array ();
				if (is_string ( $val [0] ) && strpos ( $val [0], ',' ))
					$val [0] = explode ( ',', $val [0] );
				if (is_array ( $val [0] )) {
					// 支持多个字段验证
					foreach ( $val [0] as $field )
						$_data [$field] = $data [$field];
					array_unshift ( $args, $_data );
				} else {
					array_unshift ( $args, $data [$val [0]] );
				}
				if ('function' == $val [4]) {
					return call_user_func_array ( $val [1], $args );
				} else {
					return call_user_func_array ( array (
							&$this,
							$val [1] 
					), $args );
				}
			case 'confirm' : // 验证两个字段是否相同
				return $data [$val [0]] == $data [$val [1]];
			case 'unique' : // 验证某个值是否唯一
				if (is_string ( $val [0] ) && strpos ( $val [0], ',' ))
					$val [0] = explode ( ',', $val [0] );
				$map = array ();
				if (is_array ( $val [0] )) {
					// 支持多个字段验证
					foreach ( $val [0] as $field )
						$map [$field] = $data [$field];
				} else {
					$map [$val [0]] = $data [$val [0]];
				}
				$pk = $this->getPk ();
				if (! empty ( $data [$pk] ) && is_string ( $pk )) { // 完善编辑的时候验证唯一
					$map [$pk] = array (
							'neq',
							$data [$pk] 
					);
				}
				if ($this->where ( $map )->find ())
					return false;
				return true;
			default : // 检查附加规则
				return $this->check ( $data [$val [0]], $val [1], $val [4] );
		}
	}
	
	/**
	 * 验证数据 支持 in between equal length regex expire ip_allow ip_deny
	 *
	 * @access public
	 * @param string $value
	 *        	验证数据
	 * @param mixed $rule
	 *        	验证表达式
	 * @param string $type
	 *        	验证方式 默认为正则验证
	 * @return boolean
	 */
	public function check($value, $rule, $type = 'regex') {
		$type = strtolower ( trim ( $type ) );
		switch ($type) {
			case 'in' : // 验证是否在某个指定范围之内 逗号分隔字符串或者数组
			case 'notin' :
				$range = is_array ( $rule ) ? $rule : explode ( ',', $rule );
				return $type == 'in' ? in_array ( $value, $range ) : ! in_array ( $value, $range );
			case 'between' : // 验证是否在某个范围
			case 'notbetween' : // 验证是否不在某个范围
				if (is_array ( $rule )) {
					$min = $rule [0];
					$max = $rule [1];
				} else {
					list ( $min, $max ) = explode ( ',', $rule );
				}
				return $type == 'between' ? $value >= $min && $value <= $max : $value < $min || $value > $max;
			case 'equal' : // 验证是否等于某个值
			case 'notequal' : // 验证是否等于某个值
				return $type == 'equal' ? $value == $rule : $value != $rule;
			case 'length' : // 验证长度
				$length = mb_strlen ( $value, 'utf-8' ); // 当前数据长度
				if (strpos ( $rule, ',' )) { // 长度区间
					list ( $min, $max ) = explode ( ',', $rule );
					return $length >= $min && $length <= $max;
				} else { // 指定长度
					return $length == $rule;
				}
			case 'expire' :
				list ( $start, $end ) = explode ( ',', $rule );
				if (! is_numeric ( $start ))
					$start = strtotime ( $start );
				if (! is_numeric ( $end ))
					$end = strtotime ( $end );
				return NOW_TIME >= $start && NOW_TIME <= $end;
			case 'ip_allow' : // IP 操作许可验证
				return in_array ( get_client_ip (), explode ( ',', $rule ) );
			case 'ip_deny' : // IP 操作禁止验证
				return ! in_array ( get_client_ip (), explode ( ',', $rule ) );
			case 'regex' :
			default : // 默认使用正则验证 可以使用验证类中定义的验证名称
			          // 检查附加规则
				return $this->regex ( $value, $rule );
		}
	}
	
	/**
	 * SQL查询
	 *
	 * @access public
	 * @param string $sql
	 *        	SQL指令
	 * @param mixed $parse
	 *        	是否需要解析SQL
	 * @return mixed
	 */
	public function query($sql, $parse = false) {
		if (! is_bool ( $parse ) && ! is_array ( $parse )) {
			$parse = func_get_args ();
			array_shift ( $parse );
		}
		$sql = $this->parseSql ( $sql, $parse );
		return $this->db->query ( $sql );
	}
	
	/**
	 * 执行SQL语句
	 *
	 * @access public
	 * @param string $sql
	 *        	SQL指令
	 * @param mixed $parse
	 *        	是否需要解析SQL
	 * @return false | integer
	 */
	public function execute($sql, $parse = false) {
		if (! is_bool ( $parse ) && ! is_array ( $parse )) {
			$parse = func_get_args ();
			array_shift ( $parse );
		}
		$sql = $this->parseSql ( $sql, $parse );
		return $this->db->execute ( $sql );
	}
	
	/**
	 * 解析SQL语句
	 *
	 * @access public
	 * @param string $sql
	 *        	SQL指令
	 * @param boolean $parse
	 *        	是否需要解析SQL
	 * @return string
	 */
	protected function parseSql($sql, $parse) {
		// 分析表达式
		if (true === $parse) {
			$options = $this->_parseOptions ();
			$sql = $this->db->parseSql ( $sql, $options );
		} elseif (is_array ( $parse )) { // SQL预处理
			$parse = array_map ( array (
					$this->db,
					'escapeString' 
			), $parse );
			$sql = vsprintf ( $sql, $parse );
		} else {
			$sql = strtr ( $sql, array (
					'__TABLE__' => $this->getTableName (),
					'__PREFIX__' => $this->tablePrefix 
			) );
			$prefix = $this->tablePrefix;
			$sql = preg_replace_callback ( "/__([A-Z_-]+)__/sU", function ($match) use($prefix) {
				return $prefix . strtolower ( $match [1] );
			}, $sql );
		}
		$this->db->setModel ( $this->name );
		return $sql;
	}
	
	/**
	 * 切换当前的数据库连接
	 *
	 * @access public
	 * @param integer $linkNum
	 *        	连接序号
	 * @param mixed $config
	 *        	数据库连接信息
	 * @param boolean $force
	 *        	强制重新连接
	 * @return Model
	 */
	public function db($linkNum = '', $config = '', $force = false) {
		if ('' === $linkNum && $this->db) {
			return $this->db;
		}
		
		static $_db = array ();
		if (! isset ( $_db [$linkNum] ) || $force) {
			// 创建一个新的实例
			if (! empty ( $config ) && is_string ( $config ) && false === strpos ( $config, '/' )) { // 支持读取配置参数
				$config = C ( $config );
			}
			$_db [$linkNum] = Db::getInstance ( $config );
		} elseif (NULL === $config) {
			$_db [$linkNum]->close (); // 关闭数据库连接
			unset ( $_db [$linkNum] );
			return;
		}
		
		// 切换数据库连接
		$this->db = $_db [$linkNum];
		$this->_after_db ();
		// 字段检测
		if (! empty ( $this->name ) && $this->autoCheckFields)
			$this->_checkTableInfo ();
		return $this;
	}
	// 数据库切换后回调方法
	protected function _after_db() {
	}
	
	/**
	 * 得到当前的数据对象名称
	 *
	 * @access public
	 * @return string
	 */
	public function getModelName() {
		if (empty ( $this->name )) {
			$name = substr ( get_class ( $this ), 0, - strlen ( C ( 'DEFAULT_M_LAYER' ) ) );
			if ($pos = strrpos ( $name, '\\' )) { // 有命名空间
				$this->name = substr ( $name, $pos + 1 );
			} else {
				$this->name = $name;
			}
		}
		return $this->name;
	}
	
	/**
	 * 得到完整的数据表名
	 *
	 * @access public
	 * @return string
	 */
	public function getTableName() {
		if (empty ( $this->trueTableName )) {
			$tableName = ! empty ( $this->tablePrefix ) ? $this->tablePrefix : '';
			if (! empty ( $this->tableName )) {
				$tableName .= $this->tableName;
			} else {
				$tableName .= parse_name ( $this->name );
			}
			$this->trueTableName = strtolower ( $tableName );
		}
		return (! empty ( $this->dbName ) ? $this->dbName . '.' : '') . $this->trueTableName;
	}
	
	/**
	 * 启动事务
	 *
	 * @access public
	 * @return void
	 */
	public function startTrans() {
		$this->commit ();
		$this->db->startTrans ();
		return;
	}
	
	/**
	 * 提交事务
	 *
	 * @access public
	 * @return boolean
	 */
	public function commit() {
		return $this->db->commit ();
	}
	
	/**
	 * 事务回滚
	 *
	 * @access public
	 * @return boolean
	 */
	public function rollback() {
		return $this->db->rollback ();
	}
	
	/**
	 * 返回模型的错误信息
	 *
	 * @access public
	 * @return string
	 */
	public function getError() {
		return $this->error;
	}
	
	/**
	 * 返回数据库的错误信息
	 *
	 * @access public
	 * @return string
	 */
	public function getDbError() {
		return $this->db->getError ();
	}
	
	/**
	 * 返回最后插入的ID
	 *
	 * @access public
	 * @return string
	 */
	public function getLastInsID() {
		return $this->db->getLastInsID ();
	}
	
	/**
	 * 返回最后执行的sql语句
	 *
	 * @access public
	 * @return string
	 */
	public function getLastSql() {
		return $this->db->getLastSql ( $this->name );
	}
	// 鉴于getLastSql比较常用 增加_sql 别名
	public function _sql() {
		return $this->getLastSql ();
	}
	
	/**
	 * 获取主键名称
	 *
	 * @access public
	 * @return string
	 */
	public function getPk() {
		return $this->pk;
	}
	
	/**
	 * 获取数据表字段信息
	 *
	 * @access public
	 * @return array
	 */
	public function getDbFields() {
		if (isset ( $this->options ['table'] )) { // 动态指定表名
			if (is_array ( $this->options ['table'] )) {
				$table = key ( $this->options ['table'] );
			} else {
				$table = $this->options ['table'];
			}
			$fields = $this->db->getFields ( $table );
			return $fields ? array_keys ( $fields ) : false;
		}
		if ($this->fields) {
			$fields = $this->fields;
			unset ( $fields ['_type'], $fields ['_pk'] );
			return $fields;
		}
		return false;
	}
	
	/**
	 * 设置数据对象值
	 *
	 * @access public
	 * @param mixed $data
	 *        	数据
	 * @return Model
	 */
	public function data($data = '') {
		if ('' === $data && ! empty ( $this->data )) {
			return $this->data;
		}
		if (is_object ( $data )) {
			$data = get_object_vars ( $data );
		} elseif (is_string ( $data )) {
			parse_str ( $data, $data );
		} elseif (! is_array ( $data )) {
			E ( L ( '_DATA_TYPE_INVALID_' ) );
		}
		$this->data = $data;
		return $this;
	}
	
	/**
	 * 指定当前的数据表
	 *
	 * @access public
	 * @param mixed $table        	
	 * @return Model
	 */
	public function table($table) {
		$prefix = $this->tablePrefix;
		if (is_array ( $table )) {
			$this->options ['table'] = $table;
		} elseif (! empty ( $table )) {
			// 将__TABLE_NAME__替换成带前缀的表名
			$table = preg_replace_callback ( "/__([A-Z_-]+)__/sU", function ($match) use($prefix) {
				return $prefix . strtolower ( $match [1] );
			}, $table );
			$this->options ['table'] = $table;
		}
		return $this;
	}
	
	/**
	 * USING支持 用于多表删除
	 *
	 * @access public
	 * @param mixed $using        	
	 * @return Model
	 */
	public function using($using) {
		$prefix = $this->tablePrefix;
		if (is_array ( $using )) {
			$this->options ['using'] = $using;
		} elseif (! empty ( $using )) {
			// 将__TABLE_NAME__替换成带前缀的表名
			$using = preg_replace_callback ( "/__([A-Z_-]+)__/sU", function ($match) use($prefix) {
				return $prefix . strtolower ( $match [1] );
			}, $using );
			$this->options ['using'] = $using;
		}
		return $this;
	}
	
	/**
	 * 查询SQL组装 join
	 *
	 * @access public
	 * @param mixed $join        	
	 * @param string $type
	 *        	JOIN类型
	 * @return Model
	 */
	public function join($join, $type = 'INNER') {
		$prefix = $this->tablePrefix;
		if (is_array ( $join )) {
			foreach ( $join as $key => &$_join ) {
				$_join = preg_replace_callback ( "/__([A-Z_-]+)__/sU", function ($match) use($prefix) {
					return $prefix . strtolower ( $match [1] );
				}, $_join );
				$_join = false !== stripos ( $_join, 'JOIN' ) ? $_join : $type . ' JOIN ' . $_join;
			}
			$this->options ['join'] = $join;
		} elseif (! empty ( $join )) {
			// 将__TABLE_NAME__字符串替换成带前缀的表名
			$join = preg_replace_callback ( "/__([A-Z_-]+)__/sU", function ($match) use($prefix) {
				return $prefix . strtolower ( $match [1] );
			}, $join );
			$this->options ['join'] [] = false !== stripos ( $join, 'JOIN' ) ? $join : $type . ' JOIN ' . $join;
		}
		return $this;
	}
	
	/**
	 * 查询SQL组装 union
	 *
	 * @access public
	 * @param mixed $union        	
	 * @param boolean $all        	
	 * @return Model
	 */
	public function union($union, $all = false) {
		if (empty ( $union ))
			return $this;
		if ($all) {
			$this->options ['union'] ['_all'] = true;
		}
		if (is_object ( $union )) {
			$union = get_object_vars ( $union );
		}
		// 转换union表达式
		if (is_string ( $union )) {
			$prefix = $this->tablePrefix;
			// 将__TABLE_NAME__字符串替换成带前缀的表名
			$options = preg_replace_callback ( "/__([A-Z_-]+)__/sU", function ($match) use($prefix) {
				return $prefix . strtolower ( $match [1] );
			}, $union );
		} elseif (is_array ( $union )) {
			if (isset ( $union [0] )) {
				$this->options ['union'] = array_merge ( $this->options ['union'], $union );
				return $this;
			} else {
				$options = $union;
			}
		} else {
			E ( L ( '_DATA_TYPE_INVALID_' ) );
		}
		$this->options ['union'] [] = $options;
		return $this;
	}
	
	/**
	 * 查询缓存
	 *
	 * @access public
	 * @param mixed $key        	
	 * @param integer $expire        	
	 * @param string $type        	
	 * @return Model
	 */
	public function cache($key = true, $expire = null, $type = '') {
		if (false !== $key)
			$this->options ['cache'] = array (
					'key' => $key,
					'expire' => $expire,
					'type' => $type 
			);
		return $this;
	}
	
	/**
	 * 指定查询字段 支持字段排除
	 *
	 * @access public
	 * @param mixed $field        	
	 * @param boolean $except
	 *        	是否排除
	 * @return Model
	 */
	public function field($field, $except = false) {
		if (true === $field) { // 获取全部字段
			$fields = $this->getDbFields ();
			$field = $fields ?  : '*';
		} elseif ($except) { // 字段排除
			if (is_string ( $field )) {
				$field = explode ( ',', $field );
			}
			$fields = $this->getDbFields ();
			$field = $fields ? array_diff ( $fields, $field ) : $field;
		}
		$this->options ['field'] = $field;
		return $this;
	}
	
	/**
	 * 调用命名范围
	 *
	 * @access public
	 * @param mixed $scope
	 *        	命名范围名称 支持多个 和直接定义
	 * @param array $args
	 *        	参数
	 * @return Model
	 */
	public function scope($scope = '', $args = NULL) {
		if ('' === $scope) {
			if (isset ( $this->_scope ['default'] )) {
				// 默认的命名范围
				$options = $this->_scope ['default'];
			} else {
				return $this;
			}
		} elseif (is_string ( $scope )) { // 支持多个命名范围调用 用逗号分割
			$scopes = explode ( ',', $scope );
			$options = array ();
			foreach ( $scopes as $name ) {
				if (! isset ( $this->_scope [$name] ))
					continue;
				$options = array_merge ( $options, $this->_scope [$name] );
			}
			if (! empty ( $args ) && is_array ( $args )) {
				$options = array_merge ( $options, $args );
			}
		} elseif (is_array ( $scope )) { // 直接传入命名范围定义
			$options = $scope;
		}
		
		if (is_array ( $options ) && ! empty ( $options )) {
			$this->options = array_merge ( $this->options, array_change_key_case ( $options ) );
		}
		return $this;
	}
	
	/**
	 * 指定查询条件 支持安全过滤
	 *
	 * @access public
	 * @param mixed $where
	 *        	条件表达式
	 * @param mixed $parse
	 *        	预处理参数
	 * @return Model
	 */
	public function where($where, $parse = null) {
		if (! is_null ( $parse ) && is_string ( $where )) {
			if (! is_array ( $parse )) {
				$parse = func_get_args ();
				array_shift ( $parse );
			}
			$parse = array_map ( array (
					$this->db,
					'escapeString' 
			), $parse );
			$where = vsprintf ( $where, $parse );
		} elseif (is_object ( $where )) {
			$where = get_object_vars ( $where );
		}
		if (is_string ( $where ) && '' != $where) {
			$map = array ();
			$map ['_string'] = $where;
			$where = $map;
		}
		if (isset ( $this->options ['where'] )) {
			$this->options ['where'] = array_merge ( $this->options ['where'], $where );
		} else {
			$this->options ['where'] = $where;
		}
		
		return $this;
	}
	
	/**
	 * 指定查询数量
	 *
	 * @access public
	 * @param mixed $offset
	 *        	起始位置
	 * @param mixed $length
	 *        	查询数量
	 * @return Model
	 */
	public function limit($offset, $length = null) {
		if (is_null ( $length ) && strpos ( $offset, ',' )) {
			list ( $offset, $length ) = explode ( ',', $offset );
		}
		$this->options ['limit'] = intval ( $offset ) . ($length ? ',' . intval ( $length ) : '');
		return $this;
	}
	
	/**
	 * 指定分页
	 *
	 * @access public
	 * @param mixed $page
	 *        	页数
	 * @param mixed $listRows
	 *        	每页数量
	 * @return Model
	 */
	public function page($page, $listRows = null) {
		if (is_null ( $listRows ) && strpos ( $page, ',' )) {
			list ( $page, $listRows ) = explode ( ',', $page );
		}
		$this->options ['page'] = array (
				intval ( $page ),
				intval ( $listRows ) 
		);
		return $this;
	}
	
	/**
	 * 查询注释
	 *
	 * @access public
	 * @param string $comment
	 *        	注释
	 * @return Model
	 */
	public function comment($comment) {
		$this->options ['comment'] = $comment;
		return $this;
	}
	
	/**
	 * 获取执行的SQL语句
	 *
	 * @access public
	 * @param boolean $fetch
	 *        	是否返回sql
	 * @return Model
	 */
	public function fetchSql($fetch) {
		$this->options ['fetch_sql'] = $fetch;
		return $this;
	}
	
	/**
	 * 参数绑定
	 *
	 * @access public
	 * @param string $key
	 *        	参数名
	 * @param mixed $value
	 *        	绑定的变量及绑定参数
	 * @return Model
	 */
	public function bind($key, $value = false) {
		if (is_array ( $key )) {
			$this->options ['bind'] = $key;
		} else {
			$num = func_num_args ();
			if ($num > 2) {
				$params = func_get_args ();
				array_shift ( $params );
				$this->options ['bind'] [$key] = $params;
			} else {
				$this->options ['bind'] [$key] = $value;
			}
		}
		return $this;
	}
	
	/**
	 * 设置模型的属性值
	 *
	 * @access public
	 * @param string $name
	 *        	名称
	 * @param mixed $value
	 *        	值
	 * @return Model
	 */
	public function setProperty($name, $value) {
		if (property_exists ( $this, $name ))
			$this->$name = $value;
		return $this;
	}
	/**
	 * 查询数据集
	 *
	 * @access public
	 * @param array $options
	 *        	表达式参数
	 * @return mixed
	 */
	public function selectPage($row = 20, $count = false, $options = array()) {
		if (is_string ( $options ) || is_numeric ( $options )) {
			// 根据主键查询
			$pk = $this->getPk ();
			if (strpos ( $options, ',' )) {
				$where [$pk] = array (
						'IN',
						$options 
				);
			} else {
				$where [$pk] = $options;
			}
			$options = array ();
			$options ['where'] = $where;
		}
		
		// 分析表达式
		$options = $this->_parseOptions ( $options );
		
		if ($count === false) {
			$count_options = $options;
			unset ( $count_options ['order'] );
			
			$count_options ['limit'] = 1;
			$count_options ['field'] = 'count(1) as count';
			
			$result = $this->select ( $count_options );
			$count = $result [0] ['count'];
			unset ( $result );
			unset ( $count_options );
		}
		
		// 设置分页参数名称
		$p = 'p';
		C ( 'VAR_PAGE' ) && $p = C ( 'VAR_PAGE' );
		$page = I ( $p, 1, 'intval' );
		
		if ($options ['page']) {
			$pageArr = explode ( ',', $options ['page'] );
			empty ( $pageArr [0] ) || $page = $pageArr [0];
			empty ( $pageArr [1] ) || $row = $pageArr [1];
		}
		
		$list_data ['count'] = $count;
		$list_data ['list_data'] = $this->page ( $page, $row )->select ( $options );
		
		// 分页
		if ($count > $row) {
			$page = new \Think\Page ( $count, $row );
			$page->setConfig ( 'theme', '%FIRST% %UP_PAGE% %LINK_PAGE% %DOWN_PAGE% %END% %HEADER%' );
			$list_data ['_page'] = $page->show ();
		}
		return $list_data;
	}
	// 获取多个记录的便捷方法，weiphp增加
	public function getFields($field) {
		return $this->getField ( $field, true );
	}
	/**
	 * 通用的清缓存的方法
	 *
	 * @access public
	 * @param string|array $ids
	 *        	ID值
	 * @param string $type
	 *        	清除类型，扩展使用，目前还用不上
	 * @return Model
	 */
// 	function clear($ids, $type = '', $uid = '') {
// 		is_array ( $ids ) || $ids = explode ( ',', $ids );
// 		$class = get_called_class ();
// 		$arr1 = get_class_methods ( $class );
// 		$arr2 = get_class_methods ( __CLASS__ );
// 		$methods = array_diff ( $arr1, $arr2 );
// 		$methods [] = 'getInfo';
		
// 		foreach ( $methods as $m ) {
// 			$key = $class . '::' . $m;
// 			S ( $key, null );
			
// 			foreach ( $ids as $id ) {
// 				$key_id = $key . '_' . $id;
// 				S ( $key_id, null );
// 			}
// 		}
// 	}
	/**
	 * 通用的获取单记录带缓存的方法
	 *
	 * @access public
	 * @param string $id
	 *        	ID
	 * @param boolean $update
	 *        	是否需要更新缓存
	 * @param array $data
	 *        	带此参数后不需要重复从数据库取数据更新缓存
	 * @return Model
	 */
// 	function getInfo($id, $update = false, $data = array()) {
// 		$class = get_called_class ();
// 		$key = $class . '::getInfo_' . $id;
// 		$info = S ( $key );
// 		if ($info === false || $update) {
// 			$info = ( array ) (empty ( $data ) ? $this->find ( $id ) : $data);
// 			S ( $key, $info );
// 		}
		
// 		return $info;
// 	}
}
